package com.syl.springcloud.authorization.filter;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.syl.springcloud.authorization.bean.DoubleResult;
import com.syl.springcloud.authorization.bean.UserBean;
import com.syl.springcloud.authorization.constant.LoginConstant;
import com.syl.springcloud.authorization.exception.UserInfoException;
import com.syl.springcloud.authorization.service.LoginService;
import com.syl.springcloud.authorization.util.RasUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import static com.syl.springcloud.authorization.constant.LoginConstant.DB_PASSWORD;
import static com.syl.springcloud.authorization.constant.LoginConstant.TEST_MIMA;

/**
 * 定制授权过滤器 实现更多字段登录和多种登录模式
 * supports rest login(json login) and form login
 * @author syl
 * @create 2018-08-03 14:36
 **/
@Component
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private HttpSession session;
    @Autowired
    private LoginService loginService;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 状态处理器
     * @param doubleResult
     * @param isSession
     * @param key
     * @throws UserInfoException
     */
    public void stateHandler(DoubleResult<Integer, String> doubleResult,boolean isSession,String key) throws UserInfoException {
        Integer state = doubleResult.state;
        switch (state){
            case 0:
                if(isSession)session.removeAttribute(key);
                else redisTemplate.delete(key);
                break;
            case 10:
                throw new UserInfoException(doubleResult.getData());
            case 20:
                throw new UserInfoException(doubleResult.getData());
        }
    }

    /**
     * 密码+验证码模式
     * 验证 验证码是否正确
     * @param user
     * @throws UserInfoException
     */
    public void verifyPasswordCaptcha(UserBean user) throws UserInfoException {
        String verifyCode = user.getVerifyCode();
        if(StringUtils.isBlank(verifyCode)) throw new UserInfoException("请您输入验证码!");
        String key = user.getType().getSessionKey();
        Object attribute = session.getAttribute(key);
        if(attribute == null)return;
        DoubleResult<Integer, String> doubleResult = loginService.verifyCaptcha(verifyCode, attribute.toString());
        stateHandler(doubleResult,true,key);
    }

    /**
     * 短信验证码模式
     * 验证 短信验证码是否正确
     * @param user
     * @return 根据手机号查询到的用户信息
     * @throws UserInfoException
     */
    public UserBean  verifySmsCaptcha(UserBean user) throws UserInfoException {
        String phone = user.getPhone();
        String verifyCode = user.getVerifyCode();
        if(StringUtils.isBlank(phone)) throw new UserInfoException("手机号码不能为空!");
        if(StringUtils.isBlank(verifyCode)) throw new UserInfoException("请您输入验证码!");
        String key = phone;
        if(!redisTemplate.hasKey(key))throw new UserInfoException("您输入的验证码已过期!");
        String captcha = redisTemplate.opsForValue().get(key);
        DoubleResult<Integer, String> doubleResult = loginService.verifyCaptcha(verifyCode, captcha);
        stateHandler(doubleResult,false,key);
        UserBean selectUser = new UserBean();
        if("13544082801".equals(phone)){
            // 以手机号查询用户username和password (必须加上DB_PASSWORD的前缀)
            selectUser.setUsername("user").setPassword(DB_PASSWORD +TEST_MIMA);
        }
        return selectUser;
    }

    /**
     * 分析用户类型 对不同用户类型调用不同登录接口
     * @param userBean
     * @return
     */
    private UserBean analyseUserType(UserBean userBean){
        /**
         * TODO 无论哪一种方式登录最终都需要使用密码登录
         * 例：使用手机号码登录 先依据手机号查询username,password即可
         */
        UserBean temp = new UserBean();
        String username = userBean.getUsername();
        String password = userBean.getPassword();
        switch (userBean.getType()){
            case PASSWORD:
            default:
                password = RasUtils.decrypt(password,LoginConstant.PRIVATE_KEY);
                break;
            case PASSWORD_IMAGE_CAPTCHA:
                verifyPasswordCaptcha(userBean);
                password = RasUtils.decrypt(password,LoginConstant.PRIVATE_KEY);
                break;
            case SMS_CODE:
                return verifySmsCaptcha(userBean);
        }
        temp.setUsername(username)
                .setPassword(password);
        return temp;
    }

    /**
     * 适配以json方式登录 并调用分析用户方法得到以不同模式登录方式的真实登录用户
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        session = request.getSession();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("", "");
        if(authentication != null && authentication.getPrincipal() != null){
            System.out.println("已经登录  返回登录状态");
            return authentication;
        }
        //attempt Authentication when Content-Type is json
        if(request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
                ||request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
            //use jackson to deserialize json
            try{
                InputStream is = request.getInputStream();
                UserBean temp = new Gson().fromJson(new JsonReader(new InputStreamReader(is, "UTF-8")),UserBean.class);
                UserBean user = analyseUserType(temp);// 分析并得到实际登录的用户对象
                authRequest = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            }catch (IOException e) {
                e.printStackTrace();
            }
        }// 非json 方式请求 放行
        else {
            return super.attemptAuthentication(request, response);
        }
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

}
